/**
 * Copyright (C) 2008 Christopher Allen Ogden
 *
 *     This file is part of Odysi.
 *
 *  Odysi is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Odysi is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Odysi.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "config.h"
#include "tileManager.h"
#ifndef SERVER
#include "renderSystem.h"
#endif
#include "location.h"
#include <math.h>

/**
 * Stores information relating to objects.
 */
class Object
{
    public:
        Object():_tileID(0),_id(0),_curFrame(0),_red(1.0f),_green(1.0f),_blue(1.0f),_alpha(1.0f) {}
        virtual ~Object() {}

        /**
         * Draw the object.
         *
         * @param x     X location, in pixels, to draw to.
         * @param y     Y location, in pixels, to draw to.  This will be modified by the Z
         *              value.
         * @param alpha Opacity of the object (will be used with the object's own opacity).
         */
        inline virtual void draw(int x, int y, float alpha = 1.0f) const
        {
#ifndef SERVER
            RenderSystem::GS().color(_red, _green, _blue, _alpha * alpha);
            TileManager::GS().drawTile(LEVEL_OBJECTS, _tileID, _curFrame,
                    x + _location.getXOff(),
                    y + _location.getYOff() - _location.getZ());
            RenderSystem::GS().color();
#endif
        }

        /**
         * Determine if the object is walkable.
         *
         * @return  An integer bitmap of the walkable attributes.
         */
        inline virtual int getWalkable(void) const
        {
            return TileManager::GS().getTile(LEVEL_OBJECTS, _tileID)->getWalkable();
        }

        /**
         * Get the height of the object.
         *
         * @return  The height of the object.
         */
        inline virtual unsigned int getHeight(void) const
        {
            return TileManager::GS().getTile(LEVEL_OBJECTS, _tileID)->getHeight();
        }

        /**
         * Tick.  Increments the current frame.
         */
        inline virtual void tick(void)
        {
            _curFrame++;
            if(_curFrame < 0) _curFrame = 0; // To handle wrap-around.
        }

        /**
         * Color accessors.
         */
        inline virtual float getRed(void) const { return _red; }
        inline virtual float getGreen(void) const { return _green; }
        inline virtual float getBlue(void) const { return _blue; }
        inline virtual float getAlpha(void) const { return _alpha; }
        inline virtual void setRed(float red) {
            _red = red;
            if (_red < 0) _red = 0;
            if (_red > 1.0f) _red = 1.0f;
        }
        inline virtual void setGreen(float green) {
            _green = green;
            if (_green < 0) _green = 0;
            if (_green > 1.0f) _green = 1.0f;
        }
        inline virtual void setBlue(float blue) {
            _blue = blue;
            if (_blue < 0) _blue = 0;
            if (_blue > 1.0f) _blue = 1.0f;
        }
        inline virtual void setAlpha(float alpha) {
            _alpha = alpha;
            if (_alpha < 0) _alpha = 0;
            if (alpha > 1.0f) _alpha = 1.0f;
        }

        /**
         * Frame accessors.
         */
        inline unsigned int getCurrentFrame(void) const { return _curFrame; }
        inline void setCurrentFrame(unsigned int frame) { _curFrame = frame; }

        /**
         * Location accessors.
         */
        inline Location getLocation() const { return _location; }
        inline void setX(int x) { _location.setX(x); }
        inline void setY(int y) { _location.setY(y); }
        inline void setZ(int z) { _location.setZ(z); }
        inline int getX(void) const { return _location.getX(); }
        inline int getY(void) const { return _location.getY(); }
        inline int getZ(void) const { return _location.getZ(); }

        /**
         * Offset accessors.  If force is true, do not adjust
         * location to minimize offset.
         */
        inline void setXOffset(float x, bool force = false)
        {
            int newX(0);
            if (!force) adjustOffset(&x, &newX);
            _location.setX(_location.getX() + newX);
            _location.setXOff(x);
        }
        inline void setYOffset(float y, bool force = false)
        {
            int newY(0);
            if (!force) adjustOffset(&y, &newY);
            _location.setY(_location.getY() + newY);
            _location.setYOff(y);
        }

        inline float getXOffset(void) const
        {
            return _location.getXOff();
        }

        inline float getYOffset(void) const
        {
            return _location.getYOff();
        }

        /**
         * Tile ID accessors.
         */
        inline virtual unsigned int getTileID(void) const { return _tileID; }
        inline virtual void setTileID(unsigned int tileID)
        {
            _tileID = tileID;
            _curFrame = 0;
        }   

        /**
         * Object ID accessors.
         */
        inline unsigned int getID(void) const { return _id; }
        inline void setID(unsigned int id) { _id = id; }

    private:
        /**
         * Adjust an offset.
         *
         * @param val   Value to adjust (in/out).
         * @param loc   Storage for adjusted location (in/out).
         */
        inline void adjustOffset(float *val, int *loc)
        {
            *loc = floor((*val + TILE_SIZE/2)/TILE_SIZE);
            *val = *val - *loc * TILE_SIZE;
        }

        Location _location;                 // Object location.
        unsigned int _tileID;               // Tile ID.
        unsigned int _id;                   // Object's ID.
        unsigned int _curFrame;             // Current frame.
        float _red, _green, _blue, _alpha;  // Object tint.
};
